#include "perceptron.h"
#include <sstream>
#include <iomanip>

using namespace std;
/**
Constructor por defecto
*/
Perceptron::Perceptron(){
	loadConfig(delta,nMaxIt,tasa,zero,error,maxIt,n,varW);
}

/**
Constructor de Copia
*/
Perceptron::Perceptron(Perceptron* P){
	this->tasa 	   = P->tasa;
	this->delta 	   = P->delta;
	this->nMaxIt	   = P->nMaxIt;
	this->zero 	   = P->zero;
	this->error        = P->error;
	this->maxIt        = P->maxIt;
	this->n		   = P->n; 
	this->varW	   = P->varW;
}

/**
Constructor estándar por parámetros
*/
Perceptron::Perceptron(int n,double tasa, double delta, int nMaxIt,bool zero,bool error,bool maxIt, bool varW){
	this->tasa 	   = tasa;
	this->delta 	   = delta;
	this->nMaxIt	   = nMaxIt;
	this->zero 	   = zero;
	this->error        = error;
	this->maxIt        = maxIt;
	this->n		   = n; 
	this->varW 	   = varW;
}

/**
Fase de entrenamiento del perceptrón
*/ 
void Perceptron::training(mdouble &Xe,vector<int>&ye, bool print){
	srand(time(NULL));
	vector<double> wAnt;
	wAnt.resize(n+1);
	w.resize(n+1);
	for(int z=0; z<(n+1); z++){
		w[z] = (float(rand() % 100 - 50)/100.0);
		wAnt[z] = 0;
	}	
	unsigned int longitud = Xe.size();

	vector<double> yAux;
	yAux.resize(longitud);
    	
	bool next = true;
	double errorEpocaAnt=0;
	double errorEpoca=0;
	double tacierto=0;
	float errorp=0;
	float cuenta=0;
	int nEpoca=0;

	while(next){ //mientras que la bandera next sea verdadera se ejecuta el entrenamiento
		for(unsigned int i=0; i<longitud; i++){

			yAux[i] = sign(prod_punto(Xe[i],w));
		
			float errorPatron = (ye[i]-yAux[i]);	//Error para cada patron
			errorEpoca += (errorPatron*errorPatron);	//Acumula el error de cada patron en el error total de la época
				
			for(int k=0; k<(n+1); k++){				
				w[k] = w[k]+((tasa/2)*errorPatron*Xe[i][k]);	//Corrige los pesos sinápticos
			}
				
			//Imprimo la recta	
			if(!i%10 && print){
				printWeights();
				cout<<"|W|: "<<w[0]<<", "<<w[1]<<", "<<w[2]<<endl;
			}
		}
		cuenta=0;
	        for(int z=0; z<longitud; z++){
			errorp=fabs(ye[z]-(sign(prod_punto(Xe[z],w))));
			if(errorp>0) cuenta++;
		}
		// criterios de finalizacion
	        tacierto=(1.0-cuenta/longitud);
		cout << "tacierto: " << tacierto << endl;
		errorEpoca = sqrt(errorEpoca)/longitud;
		double deltaW = 0;
		for(int z=0; z<(n+1); z++)
			deltaW += (w[z]-wAnt[z])*(w[z]-wAnt[z]);
		deltaW = sqrt(deltaW);	
		if (tacierto >= delta){
			cout << "sale por porcentaje de error: " << 100*tacierto << endl;
			next=false;
                }
		if((fabs(errorEpoca-errorEpocaAnt)<delta) && error){
			cout<<"entra dif error: "<<fabs(errorEpoca-errorEpocaAnt)<<endl;
			next=false;
		}
		if(errorEpoca<delta && error){
			cout<<"entra error: "<<errorEpoca<<endl;
			next=false;
		}

		if((errorEpoca == 0) && zero){ 
			cout<<"entra zero: "<<errorEpoca<<endl;
			next = false;
		}
		if((nEpoca>nMaxIt) && maxIt){
			cout<<"entra nEpoca: "<<nEpoca<<endl;
			next=false;	//si cumple con la condicion de mínimo error, o de error 0, entonces termina el aprendizaje
		}

		if(deltaW<tasa/1000 && varW){
			cout<<"entra varW: "<<deltaW<<endl;
			next=false;	//si cumple con la condicion de mínimo error, o de error 0, entonces termina el aprendizaje
		}
		//cout<<"|RAnt|: "<<errorEpocaAnt<<endl;	
		//cout<<"|R|: "<<errorEpoca<<endl;
		cout<<"|W|: "<<w[0]<<", "<<w[1]<<", "<<w[2]<<endl;
		
		errorEpocaAnt = errorEpoca;
		for(int z=0; z<(n+1); z++)
			wAnt[z] = w[z];	
		errorEpoca = 0;	//Setea el error de la epoca nuevamente a 0 para la proxima iteración
		nEpoca++;		
	}
	cout<<"Numero de epocas necesarias para cumplir criterio finalizacion : "<<nEpoca<<endl;
	if(print) printWeights();
}

/**
Testeo por consola
*/


void Perceptron::testWithConsole(){
	bool cont;
	do{	
		printf("\nIngrese la secuencia a testear : \n");
		vector<double> x;
		x.push_back(-1);		
		for(int j=1;j<(n+1);j++){
			cout<<"X["<<j<<"] = ";
			double aux;
			cin>>aux;
			x.push_back(aux);		
		}		
		printf("Resultado : %i ",sign(prod_punto(x,w)));
		printf("\n¿Desea testear otro valor? SI = 1 / NO = 0:  ");
		cin>>cont;
	}while(cont);
}

double Perceptron::testWithFile(mdouble &X, vector<int> &y){
	vector<double> yt = prod_mat(X,this->w);
	int total = 0;
	for(unsigned int i=0; i<yt.size(); i++){
		if(!(sign(yt[i]) == y[i]))
			total++;
	}
	cout<<endl;
	double percentage = 100-total*100/yt.size();
	cout<<"Hubo error en "<<total<<" de "<<yt.size()<<" patrones, un total del "<<percentage<<"% de aciertos"<<endl;
	return percentage;
}


/**
Manda a GNUPLOT para que grafique los valores de abcisas y pendientes de las rectas, 
mañana lo mejoramos solo esta implementado para comprobar que funciona!
*/

//-------------------------
//Global para poner replotear en la misma instancia!
GNUplot gp; 
//------------------------

void Perceptron::printWeights(){	
	//cout<<"W finales: "<<"w[0]: "<<w[0]<<", w[1]: "<<w[1]<<", w[2]: "<<w[2]<<endl;
	float m = -w[1]/w[2], b = w[0]/w[2]; 
	stringstream ss;
	ss.flags(std::ios::fixed);
	ss.precision(4);
	ss.width(0);
	ss<<"plot [-3:2][-3:2] "<<b<<"+"<<m<<"*x , 'points.txt'";
	cout<<ss.str()<< endl;
	for (int i=0;i<100000000;i++){ /*algo*/}
	//El for esta como delay, retrasa los calculos pero muestra paso a paso la recta.
	//Otra solucion sera tener un vector de stringstream con la secuencia completa y
	//recorrerla con un ciclo.
	gp(ss.str());
}
